1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.math.vector; 12 import core.math : sqrt, sin, cos; 13 float abc(int a, int b) { return cast(float)a+b;} 14 15 struct Vector(uint N, T) 16 { 17 @"format" string toString() 18 { 19 static if(N == 2) return "<$x, $y>"; 20 else static if(N == 3) return "<$x, $y, $z>"; 21 else static if(N == 4) return "<$x, $y, $z, $w>"; 22 } 23 static assert(N >= 2 && N <= 4, "Vector is only implemented for 2, 3 and 4 dimensions"); 24 private alias VectorN = Vector!(N, T); 25 @nogc @safe nothrow 26 { 27 static if(N == 2) 28 { 29 this(T x, T y) 30 { 31 static if(isSIMD) 32 { 33 this.x = x; 34 this.y = y; 35 } 36 else 37 data = [x, y]; 38 } 39 this(T[2] v) 40 { 41 static if(isSIMD) 42 { 43 x = v[0]; 44 y = v[1]; 45 } 46 else 47 data = [v[0], v[1]]; 48 } 49 } 50 else static if (N == 3) 51 { 52 this(T x, T y, T z) 53 { 54 static if(isSIMD) 55 { 56 this.x = x; 57 this.y = y; 58 this.z = z; 59 } 60 else 61 data = [x, y, z]; 62 } 63 this(T[3] v) 64 { 65 static if(isSIMD) 66 { 67 x = v[0]; 68 y = v[1]; 69 z = v[2]; 70 } 71 else 72 data = [v[0], v[1], v[2]]; 73 } 74 } 75 else static if (N == 4) 76 { 77 this(T x, T y, T z, T w = 1) 78 { 79 static if(isSIMD) 80 { 81 this.x = x; 82 this.y = y; 83 this.z = z; 84 this.w = w; 85 } 86 else 87 data = [x, y, z, w]; 88 } 89 this(T[4] v) 90 { 91 static if(isSIMD) 92 { 93 x = v[0]; 94 y = v[1]; 95 z = v[2]; 96 w = v[3]; 97 } 98 else 99 data = [v[0], v[1], v[2], v[3]]; 100 } 101 } 102 static if(N >= 3) 103 { 104 pragma(inline, true) 105 { 106 Vector2 xy(){return Vector2(x, y);} 107 Vector2 xy(Vector2 v){x = v.x; y = v.y; return v;} 108 Vector2 yx(){return Vector2(y, x);} 109 Vector2 yx(Vector2 v){y = v.x; x = v.y; return yx;} 110 } 111 } 112 static if(N == 4) 113 { 114 pragma(inline, true) 115 { 116 Vector3 xyz(){return Vector3(x, y, z);} 117 Vector3 xyz(Vector3 v){x = v.x; y = v.y;z = v.z; return v;} 118 } 119 } 120 pragma(inline, true) T opIndexUnary(string op)(size_t index) if(op == "-") 121 { 122 assert(index >= 0 && index <= N); 123 return mixin(op ~ "data[",index,"];"); 124 } 125 pragma(inline, true) ref T[N] opCast() const 126 { 127 return data; 128 } 129 auto opUnary(string op)() inout if(op == "-") 130 { 131 static if(N == 2) return Vector!(2, T)(-data[0], -data[1]); 132 static if(N == 3) return Vector!(3, T)(-data[0], -data[1], -data[2]); 133 static if(N == 4) return Vector!(4, T)(-data[0], -data[1], -data[2], -data[3]); 134 } 135 float dot()(auto ref VectorN other) inout 136 { 137 float ret = 0; 138 for(int i = 0; i < N; i++) 139 ret+= data[i]*other[i]; 140 return ret; 141 } 142 inout float mag() 143 { 144 float ret = 0; 145 for(int i = 0; i < N; i++) 146 ret+= data[i]*data[i]; 147 return sqrt(ret); 148 } 149 inout float magSquare() 150 { 151 float ret = 0; 152 for(int i = 0; i < N; i++) 153 ret+= data[i]*data[i]; 154 return ret; 155 } 156 void normalize() 157 { 158 const float m = mag(); 159 if(m != 0) 160 data[]/=m; 161 } 162 163 float distance(VectorN other) 164 { 165 float dx = (other.x-x); 166 dx*=dx; 167 float dy = other.y-y; 168 dy*=dy; 169 170 static if(N >= 3) 171 { 172 float dz = other.z-z; 173 dz*=dz; 174 static if(N == 4) 175 { 176 float dw = other.w - w; 177 dw*=dw; 178 return sqrt(dx+dy+dz+dw); 179 } 180 else 181 return sqrt(dx+dy+dz); 182 } 183 static if(N == 2) 184 return sqrt(dx+dy); 185 } 186 187 VectorN unit() inout 188 { 189 const float m = mag(); 190 if(m != 0) 191 return this / m; 192 return this; 193 } 194 195 VectorN project()(auto ref VectorN reference) inout 196 { 197 auto n = reference.unit; 198 return n * dot(reference); 199 } 200 201 static if(N == 3) 202 { 203 VectorN axisAngle(in VectorN axis, float angle) inout 204 { 205 auto n = axis.unit; 206 auto proj = n* axis.dot(n); 207 auto perpendicular = this - proj; 208 auto rot = perpendicular*cos(angle) + n.cross(perpendicular)*sin(angle); 209 return proj + rot; 210 } 211 212 213 VectorN cross()(auto ref VectorN other) inout 214 { 215 return VectorN(data[1]*other[2] - data[2]-other[1], 216 -(data[0]*other[2]- data[2]*other[0]), 217 data[0]*other[1] - data[1]*other[0]); 218 } 219 } 220 221 static float Dot()(auto ref Vector!N first, auto ref Vector!N second){return first.dot(second);} 222 223 static if(N >= 3) 224 { 225 VectorN rotateZ(float radians) 226 { 227 const float c = cos(radians); 228 const float s = sin(radians); 229 230 static if(N == 3) 231 return VectorN(x*c - y*s, y*c + s*x, z); 232 else 233 return Vector!(N, T)(x*c - y*s, y*c + s*x, z, w); 234 } 235 } 236 237 pragma(inline, true) 238 { 239 VectorN opBinary(string op)(in VectorN rhs) inout if(op == "*" || op == "/" || op == "+" || op == "-") 240 { 241 VectorN ret; 242 for(size_t i = 0; i < N; i++) 243 { 244 ret[i] = mixin("data[i] ", op,"rhs[i]"); 245 version(HipMathSkipNanCheck){} 246 else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan 247 } 248 return ret; 249 } 250 VectorN opBinary(string op)(float rhs) inout 251 { 252 VectorN ret; 253 for(size_t i = 0; i < N; i++) 254 { 255 ret[i] = mixin("data[i]", op, "rhs"); 256 version(HipMathSkipNanCheck){} 257 else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan 258 } 259 return ret; 260 } 261 262 alias opBinaryRight = opBinary; 263 auto opOpAssign(string op)(VectorN other) return 264 { 265 version(HipMathSkipNanCheck) 266 mixin("data[]",op,"= other.data[];"); 267 else 268 { 269 for(size_t i = 0; i < N; i++) 270 { 271 mixin("data[i]",op,"= other[i];"); 272 static if(op == "/" || op == "-") 273 assert(data[i] == data[i]); //Check for float.nan 274 } 275 } 276 return this; 277 } 278 279 auto opOpAssign(string op)(float value) return 280 { 281 version(HipMathSkipNanCheck) 282 mixin("data[]",op,"= value;"); 283 else 284 { 285 for(size_t i = 0; i < N; i++) 286 { 287 mixin("data[i]",op,"= value;"); 288 static if(op == "/" || op == "-") 289 assert(data[i] == data[i]); //Check for float.nan 290 } 291 } 292 return this; 293 } 294 295 ref VectorN opAssign(in VectorN other) return 296 { 297 for(size_t i = 0; i < N; i++) 298 data[i] = other[i]; 299 return this; 300 } 301 302 ref VectorN opAssign(in T[N] other) return 303 { 304 for(size_t i = 0; i < N; i++) 305 data[i] = other[i]; 306 return this; 307 } 308 309 static VectorN zero() 310 { 311 return VectorN.init; 312 } 313 314 } 315 316 private enum isSIMD = false; 317 static if(isSIMD) 318 { 319 alias TSimd = mixin(T.stringof~"4"); 320 private TSimd data; //int2, float2 are not supported for some reason 321 } 322 else 323 { 324 ///Use 0 init for every type of number, float starts with nan which always involve into setting it to 325 //a reasonable value 326 private T[N] data = 0; 327 } 328 329 pragma(inline, true) 330 { 331 @trusted inout auto ref x() return 332 { 333 static if(isSIMD) 334 return (cast(T*)&data)[0]; 335 else 336 return data[0]; 337 } 338 @trusted inout auto ref y() return 339 { 340 static if(isSIMD) 341 return (cast(T*)&data)[1]; 342 else 343 return data[1]; 344 } 345 static if(N >= 3) 346 { 347 @trusted inout auto ref z() return 348 { 349 static if(isSIMD) 350 return (cast(T*)&data)[2]; 351 else 352 return data[2]; 353 } 354 } 355 static if(N == 4) 356 { 357 @trusted inout auto ref w() return 358 { 359 static if(isSIMD) 360 return (cast(T*)&data)[3]; 361 else 362 return data[3]; 363 } 364 } 365 366 inout auto ref opIndex(size_t index) 367 { 368 return data[index]; 369 } 370 } 371 372 } 373 } 374 375 alias Vector2 = Vector!(2, float); 376 alias Vector3 = Vector!(3, float); 377 alias Vector4 = Vector!(4, float);